package com.nulldev.util.math;

import java.lang.annotation.Native;
import java.math.BigDecimal;

public class lNumber extends Number implements Comparable<lNumber> {

	@Native public static final int SIZE = 512;
	private String numbuff = "0";
	/**
	 * Integer.VALUE = 0
	 * <br>
	 * Long.VALUE = 0
	 * <br>
	 * Double.VALUE = 0.0d
	 * <br>
	 * Float.VALUE = 0.0f
	 */
	@Deprecated
	public static final lNumber NIL = fromInt(0);
	@Deprecated
	public static final lNumber LONG_MIN = fromLong(Long.MIN_VALUE);
	@Deprecated
	public static final lNumber LONG_MAX = fromLong(Long.MAX_VALUE);
	
	public lNumber() {
	}
	
	public static lNumber fromInt(final int i) {
		final lNumber n = new lNumber();
		n.setBuffer(String.valueOf(i));
		return n;
	}
	
	public static lNumber fromLong(final long i) {
		final lNumber n = new lNumber();
		n.setBuffer(String.valueOf(i));
		return n;
	}
	
	public String readNumBuff() {
		return this.numbuff;
	}
	
	private void setBuffer(final String data) {
		this.numbuff = data;
	}
	
	public lNumber add(final long i) {
		final lNumber n = clone();
		final String bd = MathUtil.lNumberUtils.add(this.readNumBuff(), String.valueOf(i)); //new BigDecimal(n.readNumBuff()).add(new BigDecimal(i));
		n.setBuffer(bd);
		return n;
	}
	
	public lNumber add(final lNumber i) {
		final lNumber n = clone();
		final String bd = MathUtil.lNumberUtils.add(this.readNumBuff(), i.readNumBuff()); //new BigDecimal(n.readNumBuff()).add(new BigDecimal(i));
		n.setBuffer(bd);
		return n;
	}
	
//	public lNumber add(final String i) {
//		final lNumber n = clone();
//		final String bd = MathUtil.lNumberUtils.add(this.readNumBuff(), i); //new BigDecimal(n.readNumBuff()).add(new BigDecimal(i));
//		n.setBuffer(bd);
//		return n;
//	}
	
	public lNumber minus(final long i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(n.readNumBuff()).subtract(new BigDecimal(i));
		n.setBuffer(bd.toString());
		return n;
	}
	
	public lNumber minus(final lNumber i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(n.readNumBuff()).subtract(new BigDecimal(i.toString()));
		n.setBuffer(bd.toString());
		return n;
	}
	
	public lNumber multiply(final long i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(n.readNumBuff()).multiply(new BigDecimal(i));
		n.setBuffer(bd.toString());
		return n;
	}
	
	public lNumber divide(final long i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(n.readNumBuff()).divide(new BigDecimal(i));
		n.setBuffer(bd.toString());
		return n;
	}
	
	public lNumber divideInv(final long i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(i).divide(new BigDecimal(n.readNumBuff()));
		n.setBuffer(bd.toString());
		return n;
	}
	
	public lNumber set(final long i) {
		final lNumber n = clone();
		final BigDecimal bd = new BigDecimal(i);
		n.setBuffer(bd.toString());
		return n;
	}

	@Override
	public double doubleValue() {
		try {
			return Double.parseDouble(this.readNumBuff());
		}catch (Exception e) {
			throw new UnsupportedOperationException("doubleValue() -> Number is larger than " + Double.MAX_VALUE + "."); 
		}
	}

	@Override
	public float floatValue() {
		try {
			return Float.parseFloat(this.readNumBuff());
		}catch (Exception e) {
			throw new UnsupportedOperationException("floatValue() -> Number is larger than " + Float.MAX_VALUE + "."); 
		}
	}

	@Override
	public int intValue() {
		try {
			return Integer.parseInt(this.readNumBuff());
		}catch (Exception e) {
			throw new UnsupportedOperationException("intValue() -> Number is larger than " + Integer.MAX_VALUE + "."); 
		}
	}

	@Override
	public long longValue() {
		try {
			return Long.parseLong(this.readNumBuff());
		}catch (Exception e) {
			throw new UnsupportedOperationException("longValue() -> Number is larger than " + Long.MAX_VALUE + "."); 
		}
	}

	@Override
	public int compareTo(final lNumber o) {
		if (o.lessThan(this)) {
			return -1;
		}else if (o.moreThan(this)) {
			return 1;
		}else {
			return 0;
		}
	}

	public lNumber clone() {
		final lNumber lx = new lNumber();
		lx.setBuffer(this.numbuff);
		return lx;
	}
	
	@Override
	public String toString() {
		return this.readNumBuff();
	}

	/**
	 * Returns the buffer plus type of data stored [u32 (Integer), u64 (Long), uExt (Custom)]
	 * @return String lNumber.***{buffer}
	 */
	public String toRString() {
		if (is32Bit()) {
			return "lNumber.u32{" + this.readNumBuff() +"}";
		}else if (is64Bit()) {
			return "lNumber.u64{" + this.readNumBuff() +"}";
		}else {
			return "lNumber.uExt{" + this.readNumBuff() +"}";
		}
	}
	
	public boolean is32Bit() {
		try {
			Integer.parseInt(this.readNumBuff());
			return true;
		}catch (Exception e) {
			return false;
		}
	}
	
	public boolean is64Bit() {
		try {
			Long.parseLong(this.readNumBuff());
			return true;
		}catch (Exception e) {
			return false;
		}
	}

	public boolean lessThanZero() {
		if (this.numbuff.contains("-")) {
			return true;
		}
		return false;
	}

	public boolean moreThanZero() {
		if (lessThanZero()) {
			return false;
		}
		if (this.numbuff.equals("0")) {
			return false;
		}
		if (this.numbuff.contains("-")) {
			return false;
		}
		return true;
	}

	public boolean lessThan(final long value) {
		return new BigDecimal(this.numbuff).compareTo(new BigDecimal(value)) == -1;
	}
	
	public boolean lessThan(final lNumber value) {
		return new BigDecimal(this.numbuff).compareTo(new BigDecimal(value.readNumBuff())) == -1;
	}

	public boolean moreThan(final long value) {
		return new BigDecimal(this.numbuff).compareTo(new BigDecimal(value)) == 1;
	}
	
	public boolean moreThan(final lNumber value) {
		return new BigDecimal(this.numbuff).compareTo(new BigDecimal(value.readNumBuff())) == 1;
	}

	/**
	 * Dangerous
	 * @param value [Number]
	 * @return lNumber
	 */
	public static lNumber overrideBuffer(final String value) {
		verifyNumber(value);
		final lNumber out = new lNumber();
		out.numbuff = value;
		return out;
	}

	public static void verifyNumber(final String value) {
		boolean passed = value != null;
		if (passed) {
			for (final char c : value.toCharArray()) {
				if (!Character.isDigit(c)) {
					passed = false;
					break;
				}
			}
		}
		if (!passed) {
			throw new UnsupportedOperationException("lNumber:verifyNumber(string) -> Input is not a number!");
		}
	}

	public void addSelf(final long l) {
		this.numbuff = this.add(l).readNumBuff();
	}

	public static lNumber nil() {
		return fromInt(0);
	}

	public void setSelf(final long l) {
		this.numbuff = String.valueOf(l);
	}
}
